/** 
 * @file primitives.h 
 * 
 * @brief  Declaration of primitives used in Prism.
 * @author Bas du Pre
 * @date   19-05-2011
 *
 * @details This file contains all primitive types that are used in Prism. Primitives
 *          are all intended as value-types, but pointers are not forbidden.
 * 
 **/ 

#pragma once

#include <stdint.h>

/// Main namespace to fence all Prism classes.
namespace Prism {
    
/** \var typedef float PFloat
 * \brief Floating point type for use in Prism applications.
 *
 * Details.
 */
/** \var typedef int32_t PInt
 * \brief Integer type for use in Prism applications.
 *
 * Details.
 */
/** \var typedef uint32_t PUInt
 * \brief Unsigned integer type for use in Prism applications.
 *
 * Details.
 */
    
    /** \todo figure out the best way to implement 64-bits types. Maybe just
     *        leave them as is.
     *  \todo size_t like  typedef.
     */
#if PRISM_BITS_32 || PRISM_DOXYGEN
    typedef float PFloat;
    typedef int32_t PInt;
    typedef uint32_t PUInt;
    typedef size_t PSizeType;
#endif
    
#if PRISM_BITS_64
    typedef double PFloat;
    typedef int64_t PInt;
    typedef uint64_t PUInt;
    typedef size_t PSizeType;
#endif

/**
 * \brief Datastructure defining a point in float precision.
 */
struct PointF {
    /// Default constructor.
    
    /// When a point is created with no initial location, it is defaulted to (0, 0).
    PointF() : x(0), y(0) { }
    
    /**
     * \brief Constructor that initializes a point with coordinate values.
     * \param x The X coordinate.
     * \param y The Y coordinate.
     */
    PointF(PFloat x, PFloat y) : x(x), y(y) { }
    /// The X coordinate.
    
    /// \default 0
    PFloat x;
    /// The Y coordinate.
    
    /// \default 0
    PFloat y;
    
    /// Point addition.
    inline PointF operator+(const PointF& b) const {
        PointF p = PointF(x, y);
        p.x += b.x;
        p.y += b.y;
        return p;
    }
    
    /// Point subtraction.
    inline PointF operator-(const PointF& b) const {
        PointF p = PointF(x, y);
        p.x -= b.x;
        p.y -= b.y;
        return p;
    }
    
    /// Point multiplication.
    inline PointF operator*(const PointF& b) const {
        PointF p = PointF(x, y);
        p.x *= b.x;
        p.y *= b.y;
        return p;
    }
    
    /// Point division.
    inline PointF operator/(const PointF& b) const {
        PointF p = PointF(x, y);
        p.x /= b.x;
        p.y /= b.y;
        return p;
    }
    
    /// PointA = PointA + PointB
    inline PointF& operator+=(const PointF& b) {
        x += b.x;
        y += b.y;
        return *this;
    }
    
    /// PointA = PointA - PointB
    inline PointF& operator-=(const PointF& b) {
        x -= b.x;
        y -= b.y;
        return *this;
    }
    
    /// PointA = PointA * PointB
    inline PointF& operator*=(const PointF& b) {
        x *= b.x;
        y *= b.y;
        return *this;
    }
    
    /// PointA = PointA / PointB    
    inline PointF& operator/=(const PointF& b) {
        x /= b.x;
        y /= b.y;
        return *this;
    }
};

/**
 * \brief Datastructure defining a point in integer precision.
 */
struct Point {
    /// Default constructor.
    
    /// When a point is created with no initial location, it is defaulted to (0, 0).
    Point() : x(0), y(0) { }
    /**
     * \brief Constructor that initializes a point with coordinate values.
     * \param x The X coordinate.
     * \param y The Y coordinate.
     */
    Point(PInt x, PInt y) : x(x), y(y) { }
    /// The X coordinate.
    
    /// \default 0
    PInt x;
    /// The Y coordinate.
    
    /// \default 0
    PInt y;
    
    /// Point addition.
    inline Point operator+(const Point& b) const {
        Point p = Point(x, y);
        p.x += b.x;
        p.y += b.y;
        return p;
    }
    
    /// Point subtraction.
    inline Point operator-(const Point& b) const {
        Point p = Point(x, y);
        p.x -= b.x;
        p.y -= b.y;
        return p;
    }
    
    /// Point multiplication.
    inline Point operator*(const Point& b) const {
        Point p = Point(x, y);
        p.x *= b.x;
        p.y *= b.y;
        return p;
    }
    
    /// Point division.
    inline Point operator/(const Point& b) const {
        Point p = Point(x, y);
        p.x /= b.x;
        p.y /= b.y;
        return p;
    }
    
    /// PointA = PointA + PointB
    inline Point& operator+=(const Point& b) {
        x += b.x;
        y += b.y;
        return *this;
    }
    
    /// PointA = PointA - PointB
    inline Point& operator-=(const Point& b) {
        x -= b.x;
        y -= b.y;
        return *this;
    }
    
    /// PointA = PointA * PointB
    inline Point& operator*=(const Point& b) {
        x *= b.x;
        y *= b.y;
        return *this;
    }
    
    /// PointA = PointA / PointB
    inline Point& operator/=(const Point& b) {
        x /= b.x;
        y /= b.y;
        return *this;
    }
};

/**
 * \brief Datastructure defining a size in integer precision.
 */
struct Size {
    /// Default constructor.
    
    /// When a Size object is created with no initial size, it is defaulted to (0, 0).
    Size() : width(0), height(0) { }
    /**
     * \brief Constructor that initializes a size with a width and height.
     * \param width The width.
     * \param height The height.
     */
    Size(PInt width, PInt height) : width(width), height(height) { }
    
    /// The width.
    PInt width;
    /// The height.
    PInt height;
};

/**
 * \brief Datastructure defining a size in float precision.
 */
struct SizeF {
    /// Default constructor.
    
    /// When a Size object is created with no initial size, it is defaulted to (0, 0).
    SizeF() : width(0), height(0) { }
    /**
     * \brief Constructor that initializes a Size with a width and height.
     * \param width The width.
     * \param height The height.
     */
    SizeF(PFloat width, PFloat height) : width(width), height(height) { }
    
    /// The width.
    PFloat width;
    /// The height.
    PFloat height;
};

/**
 * \brief Datastructure defining a rectangle in integer precision.
 */
struct Rect {
    /// Default constructor.
    Rect() { }
    /**
     * \brief Constructor that initializes a Rect with a Point as origin and a Size.
     * \param origin The origin.
     * \param size The size.
     */
    Rect(Point origin, Size size) : origin(origin), size(size) { }
    /**
     * \brief Constructor that initializes a Rect with a coordinates.
     * \param x The X coordinate of the origin.
     * \param y The X coordinate of the origin.
     * \param width The width.
     * \param height The height.
     *
     * The underlying Point and Size objects will be initialized with Point(x, y)
     * and Size(width, height).
     */
    Rect(PInt x, PInt y, PInt width, PInt height) { origin.x = x; origin.y = y; size.width = width; size.height = height; }
    /// The origin of this rectangle.
    
    /// The origin is defined as the position of the upper-left corner.
    Point origin;
    /// The size of this rectangle.
    Size size;
};

/**
 * \brief Datastructure defining a rectangle in float precision.
 */
struct RectF {
    /// Default constructor.
    RectF() { }
    /**
     * \brief Constructor that initializes a RectF with a PointF as origin and a SizeF.
     * \param origin The origin.
     * \param size The size.
     */
    RectF(PointF origin, SizeF size) : origin(origin), size(size) { }
    /**
     * \brief Constructor that initializes a RectF with a coordinates.
     * \param x The X coordinate of the origin.
     * \param y The X coordinate of the origin.
     * \param width The width.
     * \param height The height.
     *
     * The underlying Point and Size objects will be initialized with PointF(x, y)
     * and SizeF(width, height).
     */
    RectF(PFloat x, PFloat y, PFloat width, PFloat height) { origin.x = x; origin.y = y; size.width = width; size.height = height; }
    
    /// The origin of this rectangle.
    /// The origin is defined as the position of the upper-left corner.
    PointF origin;
    /// The size of this rectangle.
    SizeF size;
};

}